package com.wu.util;

import com.wu.constant.Enum.TimeUnitEnum;
import com.wu.constant.RedisConstants;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;


/**
 * @program: easy-social-parent
 * @ClassName DateUtil
 * @description:
 * @author: xianganbo
 * @create: 2021-03-26 8:32
 * @Version 1.0
 **/
//NOSONAR
@Slf4j
public class DateUtil {

    public static final String DEFAULT_FORMAT_YYYYMMDD = "yyyy-MM-dd";
    public static final String DEFAULT_FORMAT_HHMMSS = "HH:mm:ss";
    public static final String DEFAULT_FORMAT_YYYYMMDD_UNBEARABLE = "yyyyMMdd";

    /**
     * yyyyMM
     */
    private static final DateTimeFormatter MONTH_FORMATTER = DateTimeFormatter.ofPattern("yyyyMM");
    /**
     * yyyy-MM-dd
     */
    private static final DateTimeFormatter yyyyMMddEN = DateTimeFormatter.ofPattern("yyyyMMdd");
    /**
     * yyyy-MM-dd HH
     */
    private static final DateTimeFormatter yyyyMMddHH_EN = DateTimeFormatter.ofPattern("yyyy-MM-dd HH");
    /**
     * yyyy-MM-dd HH:mm
     */
    private static final DateTimeFormatter yyyyMMddHHmm_EN = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
    /**
     * yyyy-MM-dd HH:mm:ss
     */
    private static final DateTimeFormatter yyyyMMddHHmmss_EN = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    /**
     * HH:mm:ss
     */
    private static final DateTimeFormatter HHmmss_EN = DateTimeFormatter.ofPattern("HH:mm:ss");
    /**
     * yyyy年MM月dd日
     */
    private static final DateTimeFormatter yyyyMMdd_CN = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
    /**
     * yyyy年MM月dd日HH时
     */
    private static final DateTimeFormatter yyyyMMddHH_CN = DateTimeFormatter.ofPattern("yyyy年MM月dd日HH时");
    /**
     * yyyy年MM月dd日HH时mm分
     */
    private static final DateTimeFormatter yyyyMMddHHmm_CN = DateTimeFormatter.ofPattern("yyyy年MM月dd日HH时mm分");
    /**
     * yyyy年MM月dd日HH时mm分ss秒
     */
    private static final DateTimeFormatter yyyyMMddHHmmss_CN = DateTimeFormatter.ofPattern("yyyy年MM月dd日HH时mm分ss秒");
    /**
     * HH时mm分ss秒
     */
    private static final DateTimeFormatter HHmmss_CN = DateTimeFormatter.ofPattern("HH时mm分ss秒");

    private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

    // 本地时间显示格式：区分中文和外文显示
    private static final DateTimeFormatter shotDate = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT);
    private static final DateTimeFormatter fullDate = DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL);
    private static final DateTimeFormatter longDate = DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG);
    private static final DateTimeFormatter mediumDate = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM);

    /**
     * 获取当前日期
     *
     * @return yyyy-MM-dd
     * @author zero 2019/03/30
     */
    public static String getNowDate_EN() {
        return String.valueOf(LocalDate.now());
    }

    public static String getAfterDate_EN(int day) {
        return String.valueOf((LocalDate.now().plusDays(day)));
    }

    /**
     * 获取当前日期
     *
     * @return 字符串yyyy-MM-dd HH:mm:ss
     * @author zero 2019/03/30
     */
    public static String getNowTime_EN() {
        return LocalDateTime.now().format(yyyyMMddHHmmss_EN);
    }

    /**
     * 获取当前时间（yyyy-MM-dd HH）
     */
    public static String getNowTime_EN_yMdH() {
        return LocalDateTime.now().format(yyyyMMddHH_EN);
    }

    /**
     * 获取当前时间（yyyy年MM月dd日）
     */
    public static String getNowTime_CN_yMdH() {
        return LocalDateTime.now().format(yyyyMMddHH_CN);
    }

    /**
     * 获取当前时间（yyyy-MM-dd HH:mm）
     */
    public static String getNowTime_EN_yMdHm() {
        return LocalDateTime.now().format(yyyyMMddHHmm_EN);
    }

    /**
     * 获取当前时间（yyyy年MM月dd日HH时mm分）
     */
    public static String getNowTime_CN_yMdHm() {
        return LocalDateTime.now().format(yyyyMMddHHmm_CN);
    }

    /**
     * 获取当前时间（HH时mm分ss秒）
     */
    public static String getNowTime_CN_HHmmss() {
        return LocalDateTime.now().format(HHmmss_CN);
    }

    /**
     * 根据日期格式，获取当前时间
     *
     * @param formatStr 日期格式<br>
     *                  <li>yyyy</li>
     *                  <li>yyyy-MM-dd</li>
     *                  <li>yyyy-MM-dd HH:mm:ss</li>
     *                  <li>HH:mm:ss</li>
     * @return
     * @author zero 2019/03/30
     */
    public static String getTime(String formatStr) {
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern(formatStr));
    }


    /**
     * 获取中文的当前日期
     *
     * @return yyyy年mm月dd日
     * @author zero 2019/03/30
     */
    public static String getNowDate_CN() {
        return LocalDate.now().format(yyyyMMdd_CN);
    }

    /**
     * 获取中文的当前日期
     *
     * @return yyyy年mm月dd日
     * @author zero 2019/03/30
     */
    public static String getNowDate_yyyyMMddEN() {
        return LocalDate.now().format(yyyyMMddEN);
    }

    /****
     * 获取前一个的日期，格式yyyyMMdd
     * @return
     */
    public static String getYesterdayDate_yyyyMMddEN() {
        return LocalDate.now().minusDays(1).format(yyyyMMddEN);
    }
    /**
     * 得到当前系统时间(时分秒)
     *
     * @return
     */
    public static String getNowTime() {
        return LocalDate.now().format(HHmmss_EN);
    }

    /**
     * 获取中文当前时间
     *
     * @return yyyy年MM月dd日HH时mm分ss秒
     * @author zero 2019/03/30
     */
    public static String getNowTime_CN() {
        return LocalDateTime.now().format(yyyyMMddHHmmss_CN);
    }

    /**
     * 简写本地当前日期：yy-M-dd<br>
     * 例如：19-3-30为2019年3月30日
     *
     * @return 字符串yy-M-dd
     * @author zero 2019/03/30
     */
    public static String getNowLocalTime_shot() {
        return LocalDateTime.now().format(shotDate);
    }

    /**
     * 根据当地日期显示格式：yyyy年M月dd日 星期？（中国）
     *
     * @return 形如：2019年3月30日 星期六
     * @author zero 2019/03/30
     */
    public static String getNowLocalTime_full() {
        return LocalDateTime.now().format(fullDate);
    }

    /**
     * 根据当地显示日期格式：yyyy年M月dd日（中国）
     *
     * @return 形如 2019年3月30日
     * @author zero 2019/03/30
     */
    public static String getNowLocalTime_long() {
        return LocalDateTime.now().format(longDate);
    }

    /**
     * 根据当地显示日期格式：yyyy-M-dd（中国）
     *
     * @return 形如：2019-3-30
     * @author zero 2019/03/30
     */
    public static String getNowLocalTime_medium() {
        return LocalDateTime.now().format(mediumDate);
    }


    /**
     * 给指定的时间加日
     *
     * @param data 指定时间
     * @param days 要加上的日负数为减去
     * @return LocalDateTime
     */
    public static String addDays(String data, int days) {
        LocalDateTime localDateTime = formatStringToDate(data);
        return localDateTime.format(yyyyMMddHHmmss_EN);
    }

    /**
     * 给指定的时间加分钟
     *
     * @param data
     * @param minute
     * @return
     */
    public static String addMinute(String data, int minute) {
        LocalDateTime localDateTime = formatStringToDate(data);
        return localDateTime.plusMinutes(minute).format(yyyyMMddHHmmss_EN);
    }

    /**
     * 字符转换为时间，默认使用yyyy-MM-dd HH:mm:ss格式
     *
     * @param date
     * @return LocalDateTime
     */
    public static LocalDateTime formatStringToDate(String date) {
        return LocalDateTime.parse(date, yyyyMMddHHmmss_EN);
    }

    /**
     * @desc 字符串转时间戳
     * @example time="2019-04-19 00:00:00"
     **/
    public static Long getTimestamp(String time) {

        Long timestamp = 0L;
        try {
            timestamp = simpleDateFormat.parse(time).getTime();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return timestamp;
    }

    /**
     * 获取当前时间到第二天凌晨的秒数
     *
     * @return
     */
    public static Long getSecondsNextEarlyMorning() {
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DAY_OF_YEAR, 1);
        // 改成这样就好了
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.MILLISECOND, 0);
        return (cal.getTimeInMillis() - System.currentTimeMillis()) / 1000;
    }


    public static Date getNextHour() {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.add(Calendar.HOUR_OF_DAY, 1);
//		calendar.set(Calendar.MINUTE, 0);
//		calendar.set(Calendar.SECOND, 0);
        return calendar.getTime();
    }

    /**
     * 获取当前时间到第二天早上6点的秒数
     */
    public static Long getSecondsNextLiuMorning() {
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DAY_OF_YEAR, 1);
        // 改成这样就好了
        cal.set(Calendar.HOUR_OF_DAY, 6);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.MILLISECOND, 0);
        Long secondsNextLiuMorning = (cal.getTimeInMillis() - System.currentTimeMillis()) / 1000;
        if (secondsNextLiuMorning > RedisConstants.DAY_1) {
            secondsNextLiuMorning = secondsNextLiuMorning - RedisConstants.DAY_1;
        }
        return secondsNextLiuMorning;
    }

    /**
     * 字符串解析成时间对象
     *
     * @param dateTimeString String
     * @param pattern        StrUtils.DATE_TIME_PATTERN || StrUtils.DATE_PATTERN，如果为空，则为yyyy-MM-dd
     * @return
     * @throws ParseException
     */
    public static Date dateParse(String dateTimeString, String pattern) throws ParseException {
        if (StringUtils.isBlank(pattern)) {
            pattern = DEFAULT_FORMAT_YYYYMMDD;
        }
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        return sdf.parse(dateTimeString);
    }

    /**
     * @description: 获取时间段内 每天int
     * @author: xiaoxing
     * @date: 2022/11/11 16:50
     * @param: [beginTime, endTime]  20221101,20221111
     **/
    public static List<Integer> getTimeInt(Integer beginTime, Integer endTime) throws ParseException {
        List<Integer> dates = new ArrayList<>();
        Date begin = dateParse(String.valueOf(beginTime), DEFAULT_FORMAT_YYYYMMDD_UNBEARABLE);
        Date end = dateParse(String.valueOf(endTime), DEFAULT_FORMAT_YYYYMMDD_UNBEARABLE);
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(begin);
        dates.add(beginTime);
        while (end.after(calendar.getTime())) {
            calendar.add(Calendar.DAY_OF_MONTH, 1);
            dates.add(Integer.valueOf(format(calendar.getTime(), DEFAULT_FORMAT_YYYYMMDD_UNBEARABLE)));
        }
        return dates;
    }


    public static String format(Date date, String pattern) {
        SimpleDateFormat formator = new SimpleDateFormat(pattern);
        return formator.format(date);
    }


    /**
     * 时间加减天数
     *
     * @param startDate 要处理的时间，Null则为当前时间
     * @param days      加减的天数
     * @return Date
     */
    public static Date dateAddDays(Date startDate, int days) {
        if (startDate == null) {
            startDate = new Date();
        }
        Calendar c = Calendar.getInstance();
        c.setTime(startDate);
        c.set(Calendar.DATE, c.get(Calendar.DATE) + days);
        return c.getTime();
    }
    /**
     * 时间加减年数
     *
     * @param startDate 要处理的时间，Null则为当前时间
     * @param years      加减的年
     * @return Date
     */
    public static Date dateAddYears(Date startDate, int years) {
    	if (startDate == null) {
    		startDate = new Date();
    	}
    	Calendar c = Calendar.getInstance();
    	c.setTime(startDate);
    	c.set(Calendar.YEAR, c.get(Calendar.YEAR) + years);
    	return c.getTime();
    }

    /****
     * 获取有效时长（当前时间到有效日期的秒数）
     * @param effectiveTime 有效日期
     * @return
     */
    public static long getEffectiveDuration(String effectiveTime) {
    	try {
           return (simpleDateFormat.parse(effectiveTime).getTime() - new Date().getTime()) / 1000;
        } catch (ParseException e) {
            e.printStackTrace();
        }
    	return 0L;
    }

    /****
     * 获取当天的开始时间
     * @return
     */
	public static Date getCurrentDateStartTime() {
		Calendar currentDate = Calendar.getInstance();
		currentDate.set(Calendar.HOUR_OF_DAY, 0);
		currentDate.set(Calendar.MINUTE, 0);
		currentDate.set(Calendar.SECOND, 0);
		return currentDate.getTime();
	}
	
	/****
	 * 获取前一天的开始时间
	 * @return
	 */
	public static Date getYesterdayStartTime() {
		Calendar yesterday = Calendar.getInstance();
        yesterday.add(Calendar.DAY_OF_MONTH, -1);
        yesterday.set(Calendar.HOUR_OF_DAY, 0);
        yesterday.set(Calendar.MINUTE, 0);
        yesterday.set(Calendar.SECOND, 0);
		return yesterday.getTime();
	}

	/***
	 * 获取指定日期的结束时间戳
	 * @param dateStr
	 * @return
	 */
	public static long getEndOfDayTimestamp(String dateStr) {
		try {
			// 将输入的日期字符串解析为 LocalDate 对象
			LocalDate date = LocalDate.parse(dateStr, yyyyMMddEN);

			// 获取该日期的结束时间（23:59:59.999）
			LocalDateTime endOfDay = date.atTime(23, 59, 59, 999_000_000);

			// 转换为毫秒时间戳

            return endOfDay.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
		} catch (Exception e) {
			log.warn("时间格式异常 error = " + e);
		}
		return 0;
	}

	/****
	 * 字符串转date
	 * @return
	 */
	public static Date getStrToDate(String date) {
		try {
			return dateFormat.parse(date);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return null;
	}

    /**
     * 获取连个时间戳相差的天数
     * @param day1 较大的时间戳
     * @param day2 较小的时间戳
     * @return
     */
	public static Long getDateDiff(Long day1, Long day2){
        return (day1 - day2) / 86400 + 1;
    }

    /**
     * 获取两个时间戳相差天数（aviator提供）
     * 
     * @param day1 较大的时间戳
     * @param day2 较小的时间戳
     * @return
     */
    public static Long getDateDiffForAviator(Long day1, Long day2) {
        return (day1 - day2) / 86400 - 1;
    }

    /**
     * 将时间戳转换成UTC时间
     * @param time
     * @return
     */
    public static Date formatToUTCDate(long time) {
        Instant instant = Instant.ofEpochMilli(time);
        ZonedDateTime zonedDateTime = instant.atZone(ZoneId.of("UTC"));
        // 将ZonedDateTime转换为Date
        Date utcDate = Date.from(zonedDateTime.toInstant());
        return utcDate;
    }

    /****
     * 获取当前日期所在周的日期字符串，从周日开始到周六结束，格式为 yyyyMMdd。
     * @return
     */
    public static String getCurrentWeekSunday() {
        LocalDate sunday = LocalDate.now().with(TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY));
        return sunday.format(yyyyMMddEN);
    }

    /****
     * 获取当前月字符串
     * @return
     */
    public static String getCurrentMonth() {
        return LocalDate.now().format(MONTH_FORMATTER);
    }

    /**
     * 获取指定日期所在周的日期字符串，从周日开始到周六结束，格式为 yyyyMMdd。
     *
     * @param dateStr 指定日期字符串，格式为 yyyyMMdd
     * @return 周日期字符串列表
     */
	public static List<String> getWeekDates(String dateStr) {
		// 将输入的日期字符串解析为 LocalDate 对象
		LocalDate date = LocalDate.parse(dateStr, yyyyMMddEN);

		// 获取当前日期
		LocalDate today = LocalDate.now();

		// 获取该日期所在周的周日
		LocalDate sunday = date.with(TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY));

		// 创建一个列表来存储周日期字符串
		List<String> weekDates = new ArrayList<>();

		// 判断输入日期是否在当前周内
		boolean isCurrentWeek = !date.isBefore(sunday) && date.isBefore(today.plusDays(1));

		// 从周日开始，逐天增加，直到周六或昨天
		for (int i = 0; i < 7; i++) {
			LocalDate day = sunday.plusDays(i);
			if (isCurrentWeek && day.isEqual(today)) {
				break; // 如果是当前周且日期等于今天，则停止添加
			}
			weekDates.add(day.format(yyyyMMddEN));
		}

		return weekDates;
	}

    /**
     * 获取指定月份的所有日期集合，格式为 yyyyMMdd。
     *
     * @param monthStr 指定月份字符串，格式为 yyyyMM
     * @return 月份日期字符串列表
     */
	public static List<String> getMonthDates(String monthStr) {
	    // 定义日期格式
	    // 将输入的月份字符串解析为 YearMonth 对象
	    YearMonth yearMonth = YearMonth.parse(monthStr, MONTH_FORMATTER);

	    // 获取该月份的第一天和最后一天
	    LocalDate firstDay = yearMonth.atDay(1);
	    LocalDate lastDay = yearMonth.atEndOfMonth();

	    // 获取当前日期
	    LocalDate today = LocalDate.now();

	    // 创建一个列表来存储月份日期字符串
	    List<String> monthDates = new ArrayList<>();

	    // 判断输入月份是否是当前月
	    boolean isCurrentMonth = yearMonth.equals(YearMonth.from(today));

	    // 从第一天开始，逐天增加，直到最后一天或昨天
	    for (LocalDate date = firstDay; !date.isAfter(lastDay); date = date.plusDays(1)) {
	        if (isCurrentMonth && date.isEqual(today)) {
	            break; // 如果是当前月且日期等于今天，则停止添加
	        }
	        monthDates.add(date.format(yyyyMMddEN));
	    }

	    return monthDates;
	}

    /***
     * 日期格式转换
     * @param weekStartDayStr 格式：yyyyMMdd
     * @return
     */
    public static LocalDate parseLocalDate(String weekStartDayStr) {
    	return LocalDate.parse(weekStartDayStr, yyyyMMddEN);
    }

    /****
     * 获取指定月份的第一天
     * @param month  格式：yyyyMM
     * @return
     */
    public static LocalDate getMonthFirstDay(String month) {
    	return LocalDate.parse(month + "01", yyyyMMddEN);
    }

    public static LocalDate getLocaalDateFromLong (Long time) {
    	if (ObjectUtils.isNotEmpty(time)) {
    		try {
    			// 任务开始日期和结束日期
    			return LocalDate.ofInstant(Instant.ofEpochMilli(time), ZoneId.of("Asia/Shanghai"));
    		} catch (Exception e) {
				log.warn("时间戳转localdate异常 error = " + e);
			}
    	}
    	// 当前日期（使用北京时间）
        return LocalDate.now(ZoneId.of("Asia/Shanghai"));
    }
    /**
	 * 获取当前月份的结束时间戳（精确到秒）
	 *
	 * @return 当前月份的结束时间戳
	 */
	public static long getEndOfMonthTimestamp() {
	    // 获取当前日期
	    LocalDate now = LocalDate.now();
	    // 获取当前月份的最后一天
	    LocalDate lastDayOfMonth = now.with(TemporalAdjusters.lastDayOfMonth());
	    // 设置时间为23:59:59
	    LocalDateTime endOfMonth = lastDayOfMonth.atTime(23, 59, 59);
	    // 转换为时间戳
	    return (endOfMonth.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() - System.currentTimeMillis()) / 1000;
	}
    public static Date getTimByUnitAndDuration(Date date, String timeUnit, Integer duration){
        TimeUnitEnum name = TimeUnitEnum.getByName(timeUnit);
        return switch (name) {
            case MINUTE -> cn.hutool.core.date.DateUtil.offsetMinute(date, duration);
            case HOUR -> cn.hutool.core.date.DateUtil.offsetHour(date, duration);
            case DAY -> cn.hutool.core.date.DateUtil.offsetDay(date, duration);
            case MONTH -> cn.hutool.core.date.DateUtil.offsetMonth(date, duration);
            case YEAR -> cn.hutool.core.date.DateUtil.offsetMonth(date, duration * 12);
            default -> throw new IllegalArgumentException("timeUnit must be MINUTE, HOUR, DAY, MONTH, YEAR");
        };
    }


}
